const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_FULFTLLED = "PROMISE_STATUS_FULFTLLED";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

function withThrow(fn, v, res, rej) {
  try {
    let result = fn(v);
    res(result);
  } catch (error) {
    rej(error);
  }
}

class Mypromise {
  status = PROMISE_STATUS_PENDING;
  value;
  reason;
  onfulfilledfns = [];
  onrejectedfns = [];
  constructor(executor) {
    const resolve = (value) => {
      process.nextTick(() => {
        if (this.status === PROMISE_STATUS_PENDING) {
          this.status = PROMISE_STATUS_FULFTLLED;
          this.value = value;
          this.onfulfilledfns.forEach((fn) => {
            fn(this.value);
          });
        }
      });
    };

    const reject = (reason) => {
      process.nextTick(() => {
        if (this.status === PROMISE_STATUS_PENDING) {
          this.status = PROMISE_STATUS_REJECTED;
          this.reason = reason;
          this.onrejectedfns.forEach((fn) => {
            fn(this.reason);
          });
        }
      });
    };
    executor(resolve, reject);
  }

  then = (onfulfilled, onrejected) => {
    onfulfilled = onfulfilled || ((value) => value);
    onrejected =
      onrejected ||
      ((err) => {
        throw err;
      });
    return new Mypromise((resolve, reject) => {
      if (this.status == PROMISE_STATUS_FULFTLLED)
        withThrow(onfulfilled, this.value, resolve, reject);

      if (this.status == PROMISE_STATUS_REJECTED)
        withThrow(onrejected, this.reason, resolve, reject);

      if (this.status == PROMISE_STATUS_PENDING) {
        this.onfulfilledfns.push(() =>
          withThrow(onfulfilled, this.value, resolve, reject)
        );
        this.onrejectedfns.push(() =>
          withThrow(onrejected, this.reason, resolve, reject)
        );
      }
    });
  };

  catch = (onrejected) => {
    return this.then(undefined, onrejected);
  };

  static all = (promises) => {
    return new Mypromise(() => {
      let result = [];
      for (const promise of promises) {
        promise
          .then((res) => {
            result.push(res);
            if (result.length == promises.length) {
              resolve(result);
            }
          })
          .catch((err) => {
            reject(err);
          });
      }
    });
  };

  static resolve = (value) => {
    return new Mypromise((resolove) => {
      resolove(value);
    });
  };

  static reject = (err) => {
    return new Mypromise((resolve, reject) => {
      reject(err);
    });
  };

  static race = (promises) => {
    return new Mypromise((resolve, reject) => {
      for (const promise of promises) {
        promise.then(resolve, reject);
      }
    });
  };
}
var promise = new Mypromise((resolve, reject) => {
  reject("error");
});

promise
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
    return 111;
  })
  .then((res) => {
    console.log(res);
  });
